<template>
  <svg id="graph" ref="searchBoard"></svg>
</template>

  
  <script>
  import {mapActions} from 'vuex'
  import {deepCopy} from '../../../common/utils/object'
  import {consoleGroup} from '../../../common/utils/message'
  import config from '../../graph/utils/config'
  import {getGridLayout, calcScale} from '../../graph/utils/layout'
  import GraphEditorItem from "../../../modules/graph/components/GraphEditorItem";
  import bus from "../../graph/utils/eventBus";
  
//   import GraphEditorGroup from "./GraphEditorGroup";
  
  
  export default {
  
  
    components: {
      GraphEditorItem
    },
  
    name: 'EntitySearchBoard',
    props: {
      data: {
        type: Object
      },

    },
    data() {
      return {
        origin: null,
        config: {...config},
        projectInfo: null,
        projectId: 0,
        layouts: null,
        layoutMode: 'FORCE',
        s: [],
  
        //wx
        colorJudge: [],
        forNumber: 1,
        middleLinks: [],
        allNodes: [],
        hasDoubleClicked: [],
  
        links: [],
        delenodes: [],
        delelinks: [],
        focusNodes: [],
        selectedGroups: [],
        svgElements: {
          simulation: null,
          svg: null,
          defs: null,
          marker: null,
          view: null,
          root: null,
          focusGroup: null,
  
          //wx
          focusGroup_wx: null,
  
          svgLinks: null,
          svgLinksText: null,
          svgNodes: null,
          svgNodesText: null,
          svgMenuText: null,
          boundDrag: null,
          boundZoom: null,
          scale: null
        },
        flags: {
          loaded: false,
          singleFocus: true,
          enableFocus: true,
          pinned: false,
          locked: false,
          selectingMode: false
        },
        graphData: null,
        selectedItem: {
          type: null,
          item: {}
        },
        
      }
    },
  
    methods: {
  
      // ...mapActions(['updateLayout', 'saveAsPng', 'saveAsXml']),
  
      //wx
      ...mapActions({
        createItemAct: 'createItem',
        updateItemAct: 'updateItem',
        deleteItemNodeAct: 'deleteItemNode',
        // showMore: 'showMore',
        updateLayout: 'updateLayout',
        saveAsPng: 'saveAsPng',
        saveAsXml: 'saveAsXml',
        entityQueryGlobal:'entityQueryGlobal',
        getProjectInfoAct: 'getProjectInfo',
      }),
      /***** 重新挂载图谱 *****/
      mountGraphData(data) {
        // console.log('projectInfo:', this.projectId)
        console.log('daataaaaa'+data)
        // this.origin = deepCopy(data)
        // this.projectInfo = this.getProjectInfoAct(this.projectId)
        const {nodes, links, layouts} = data
        this.nodes = [...nodes]
        console.log("项目所有节点849561：" + JSON.stringify(this.nodes))
        this.links = [...links]
        this.layouts = deepCopy(layouts)
  
        // console.log("布局验证：" + JSON.stringify(this.layouts))
  
        // consoleGroup('mountGraphData', () => {
        //   console.log('nodes:', this.nodes)
        //   console.log('links:', this.links)
        //   console.log('layouts:', this.layouts)
        // })
  
        this.init()
        this.flags.loaded = true
      },
      /***** 图谱绘制 *****/
      // 初始化图谱
      init() {
        const {
          // 全局变量 & 配置
          $d3,
          $el,
          config: {width, height},
          // 子例程
          reset,
          setSimulation,
          setView,
          setRoot,
          setFocusGroup,
          setLinks,
          setLinksText,
          setNodes,
          setNodesText,
          setMenuCircle,
          // 力导图绑定事件
          setDrag,
          setZoom,
          tick,
          // 图谱操作
          resetZoom,
          setEnableFocus,
          restoreLayout,
  
        } = this
        // 清除图谱
        reset()
        setEnableFocus(false)
  
        this.middleLinks = this.links
  
        const simulation = setSimulation()
  
        const svg = $d3
          .select($el)
          .attr('viewBox', [-width / 2, -height / 2, width, height])
        this.svgElements.svg = svg
  
        // 设置 defs 定义组
        // setDefs(svg)
  
        // 设置透明操作板
        const view = setView(svg)
        const root = setRoot(svg)
  
        // const g =  svg.append('g')
        //   .attr('transform', 'translate(' + marge.top + ',' + marge.left + ')')
        //   .attr('class', 'container')
        // this.linksText = g.append('g')
        //   .selectAll('text')
        //   .data(edges)
        //   .enter()
        //   .append('text')
        //   .attr('class', 'linksText')
        //   .text(function (d) {
        //     return d.relations
        //   })
        //   .style('font-size', 14)
        //   .attr('fill-opacity', 0)
  
        const boundZoom = setZoom(root)
        view.call(boundZoom)
  
        // 设置组
        setFocusGroup(root)
  
        // 设置关系、关系文字
        setLinks(root)
        setLinksText(root)
  
  
        const boundDrag = setDrag(simulation)
        const scale = $d3.scaleOrdinal($d3.schemeCategory10)
        this.svgElements.scale = scale
        this.$emit('init-property', 'nodeScale', scale)
        //设置图片
        // drawPattern(root)
        // 设置节点、节点文字
        setNodes(root, boundDrag, scale)
        setNodesText(root, boundDrag)
  
        //设置环形菜单
        setMenuCircle(root)
  
        simulation.on('tick', tick)
  
        // setTimeout(() => {
        //   resetZoom()
        // }, 300)
        setTimeout(() => {
          resetZoom()
        }, 600)
        setEnableFocus(true)
        restoreLayout()
      },
      // 重置图谱节点
      reset() {
        const {view, root} = this.svgElements
        if (view) view.remove()
        if (root) root.remove()
      },
      // 设置力导图初始化
      setSimulation() {
        const {
          $d3,
          nodes,
          links,
          config: {baseRadius}
        } = this
        const simulation = $d3
          .forceSimulation(nodes)
          .force(
            'link',
            $d3
              .forceLink(links)
              .id(d => d.id)
              .distance(
                d => (d.source.radius + d.target.radius) * 30 + baseRadius
              )
          )
          .force(
            'charge',
            $d3.forceManyBody().strength(d => -1000 - d.radius * 300)
          )
          .force('x', $d3.forceX())
          .force('y', $d3.forceY())
        this.svgElements.simulation = simulation
        return simulation
      },
      setDefs(svg) {
        const defs = this.svgElements.defs || svg.append('defs')
        this.svgElements.defs = defs
        return defs
      },
      // 设置拖曳背景图
      setView(svg) {
        const {width, height} = this.config
        const view = svg
          .append('rect')
          .attr('class', 'view')
          .style('fill', 'transparent')
          .attr('x', -width / 2)
          .attr('y', -height / 2)
          .attr('width', width)
          .attr('height', height)
          .on('click', this.clickView)
        this.svgElements.view = view
        return view
      },
      // 设置图谱根节点
      setRoot(svg) {
        const root = svg.append('g').attr('class', 'root')
        this.svgElements.root = root
        return root
      },
  
  
      // 设置图谱高亮组
      setFocusGroup(root) {
        const focusGroup = root
          .append('g')
          .attr('class', 'focus')
          .attr('stroke', 'skyblue')
          .attr('stroke-width', 20)
        this.svgElements.focusGroup = focusGroup
      },
  
      //设置环形菜单
      setMenuCircle(root) {
        const svgMenuText = root
          .append('g')
          .attr('class', 'Circle')
        this.svgElements.svgMenuText = svgMenuText
      },
      setLinks(root) {
        const {links, clickLink, $d3} = this
  
        // console.log("root打印：" + JSON.stringify(root))
  
        //修改箭头位置
        const arrowMarker = root
          .append('g')
          .append("defs")
          .append("marker")
          .attr("id", "arrow")
          .attr("viewBox", "0 -5 10 10")//坐标系的区域
          // .selectAll('line')
          // .data(links)
          // .join('line')
          // .attr("refX",function (d) { 
          //   if(d.radius){
          //     console.log("dddd"+d.radius)
          //   var res = 40 + (d.radius - 5)* 8.5
          //  console.log("dddd"+res)
          //   return res
          //   }else{
          //     console.log("不知道半径"+d.radius)
          //     return 74
          //   } 
          //   })
      // 1,40 3，57  4，65，5，74
          .attr("refX", 74)//箭头坐标
          
          .attr("refY", 0)
          .attr("markerWidth", 12)//标识的大小
          .attr("markerHeight", 12)
          // .attr("markerUnits", "strokeWidth")
          // .attr("markerWidth", "5")
          // .attr("markerHeight", "5")
          // .attr("viewBox", "0 0 12 12")
          // .attr("refX", 69)
          // .attr("refY", "6")

          
          .attr("orient", "auto");
  
        const arrow_path = "M0,-5L10,0L0,5";
        // const arrow_path = "M2,2 L10,6 L2,10 L6,6 L2,2";
        arrowMarker.append("path")
          .attr("d", arrow_path)
          .attr("fill", "#999 ");
  
  
        // 设置关系线段
        const svgLinks = root
          .append('g')
          .attr('class', 'links')
          .attr('stroke', '#999')
          .attr('stroke-opacity', 1)
          .selectAll('line')
          .data(links)
          .join('line')
          .attr('stroke-width', d => d.value )
          .attr('id', d => `link-${d.id}`)
          .attr('data-id', d => d.id)
          // .attr('marker-end', d => `url(#arrow-${d.id})`)
          .attr("marker-end", "url(#arrow)")
          .attr("arrowValue", d => d.value)
          .on('click', clickLink)
        svgLinks.append('title').text(d => d.name)
        // arrowMarker.attr("refX", svgLinks.attr("a"));
        this.svgElements.svgLinks = svgLinks
        return svgLinks
      },
      setLinksText(root) {
        const {
          config: {font, fontSize},
          setLinksPositionRaio,
          links,
          clickLink
        } = this
        const svgLinksText = root
          .append('g')
          .attr('class', 'links_text')
          .selectAll('text')
          .data(links)
          .join('text')
          .style('fill', '#ffffff')
          .style('font', `${fontSize}px ${font}`)
          .style('user-select', 'none')
          .attr('dominant-baseline', 'middle')
          .attr('text-anchor', 'middle')
          .attr('data-id', d => d.id)
         
          .on('click', clickLink)
        this.svgElements.svgLinksText = svgLinksText
        // .text(d => d.name)
        setLinksPositionRaio()
  
        return svgLinksText
      },
  
      // 绘制图片
      drawPattern() {
        console.log("执行了背景设置方法")
        const {
          config: {baseRadius},
          nodes,
          focus,
          unfocus,
          clickNode,
          root
        } = this
  
        let gPattern = root.append("g").attr("class", "g-pattern");
  
        //  添加pattern
        var pattern = gPattern.selectAll("pattern").data(nodes, function (d) {
          return "pattern" + d.id;
        });
  
        // 赋予宽高
        pattern
          .enter()
          .append("pattern")
          .attr("id", function (d) {
            return "pattern" + d.id;
          })
          .attr("x", 0)
          .attr("y", 0)
          .attr("height", function (d) {
            var imageHeight;
            // if (d.group === "态势") {
            if (d.group === "一级分类") {
              imageHeight = 78;
            } else if (d.group === "参与方") {
              imageHeight = 60;
            } else if (d.group === "作战单元") {
              imageHeight = 45;
            } else {
              imageHeight = 55;
            }
            return imageHeight
          })
          .attr("width", function (d) {
            var imageWidth;
            // if (d.group === "态势") {
            if (d.group === "一级分类") {
              imageWidth = 78;
            } else if (d.group === "参与方") {
              imageWidth = 60;
            } else if (d.group === "作战单元") {
              imageWidth = 45;
            } else {
              imageWidth = 55;
            }
            return imageWidth
          })
          .append("image")
          .attr("preserveAspectRatio", "none")
          .attr("xlink:href", function (d) {
            console.log("imagePath2555:" + d.group)
            var imagePath = "";
            // if (d.group === "态势") {
            if (d.group === "一级分类") {
              imagePath = require("@/assets/tsh.png");
            } else if (d.group === "参与方") {
              imagePath = require("@/assets/cyf.png");
            } else if (d.group === "作战单元") {
              imagePath = require("@/assets/jc.png");
            } else {
              imagePath = require("@/assets/airfw.png");
            }
            return imagePath
          })
          .attr("x", 0)
          .attr("y", 0)
          .attr("height", function (d) {
            return (baseRadius + d.radius * 10) * 2;
          })
          .attr("width", function (d) {
            return (baseRadius + d.radius * 10) * 2
          });
      },
      setNodes(root, boundDrag, scale) {
        const {
          config: {baseRadius},
          nodes,
          focus,
          unfocus,
          clickNode,
          doubleClick,
          drawPattern,
          rightClickNode
        } = this
  
        nodes.filter(node => {
          node.isDelete = false
          node.isNew = false
          // console.log("456测试wx：" + JSON.stringify(node))
        })
  
        const svgNodes = root
          .append('g')
          .attr('class', 'nodes')
          .attr('stroke', '#3aa6b5')
          .attr('stroke-width', 0)
          .selectAll('circle')
          .data(nodes)
          .join('circle')
          .attr('r', d => baseRadius + d.radius * 10)
          .attr('fill', d => (d.color ? d.color : scale(d.group)))
          //.style('fill',function(d){ return "url(#" + "pattern" + d.id + ")"; })
          .attr('data-id', d => d.id)
          .call(boundDrag)
          .on('click', clickNode)
          .on('contextmenu', rightClickNode)
          .on('mouseover', focus)
          .on('mouseout', unfocus)
          .on('dblclick', doubleClick)
        svgNodes.append('title').text(d => d.name)
        this.svgElements.svgNodes = svgNodes
        return svgNodes
      },
      setNodesText(root, boundDrag) {
        const {
          $d3,
          config: {font},
          nodes,
          clickNode,
          focus,
          unfocus,
          doubleClick,
          rightClickNode,
        } = this
        const svgNodesText = root
          .append('g')
          .attr('class', 'nodes_text')
          .selectAll('text')
          .data(nodes)
          .join('text')
          .style('fill', '#ffffff')
          .style('font', d => `${d.textSize}px ${font}`)
          .style('user-select', 'none')
          .attr('dominant-baseline', 'middle')
          .attr('text-anchor', 'middle')
          .attr('data-id', d => d.id)
          .text(d => d.name)
          .call(boundDrag)
          .on('click', clickNode)
          .on('contextmenu', rightClickNode)
          .on('dblclick', doubleClick)
          .on('mouseover', focus)
          .on('mouseout', unfocus)
        this.svgElements.svgNodesText = svgNodesText
  
        // console.log("493调试：" + JSON.stringify($d3.selectAll('root').size()))
  
        return svgNodesText
      },
      // 设置 focus 节点
      setFocus(node) {
        const {
          svgElements: {focusGroup: fg},
          flags: {enableFocus},
          setNodeHighLight,
          updateHighLightNodes
        } = this
        this.clearFocus()
        if (!enableFocus) return
  
        if (fg.select(`#focus-node-${node.id}`).empty()) {
          setNodeHighLight(node, true)
          updateHighLightNodes()
        }
      },
      updateHighLightNodes() {
        const {
          config: {baseRadius},
          nodes,
          svgElements: {focusGroup: fg}
        } = this
  
        const originFocus = fg.selectAll('circle')
        fg.selectAll('circle')
          .data(nodes.filter(node => node.highLight || node.focus))
          .join('circle')
          .attr('r', d => baseRadius + d.radius * 10 + 5)
          .attr('fill', 'none')
          .attr('id', d => `focus-node-${d.id}`)
          .attr('cx', d => d.x)
          .attr('cy', d => d.y)
          .merge(originFocus)
      },
      // 力导图更新
      tick() {
        const {
          focusGroup,
          svgLinks,
          svgLinksText,
          svgNodes,
          svgNodesText,
          svgMenuText
        } = this.svgElements
  
        svgLinks
          .attr('x1', d => d.source.x)
          .attr('y1', d => d.source.y)
          .attr('x2', d => d.target.x)
          .attr('y2', d => d.target.y)
  
        svgLinksText
          .attr('x', d => {
            const {
              source: {x: x1},
              target: {x: x2},
              positionRatio
            } = d
            return x1 + (x2 - x1) * positionRatio
          })
          .attr('y', d => {
            const {
              source: {y: y1},
              target: {y: y2},
              positionRatio
            } = d
            return y1 + (y2 - y1) * positionRatio
          })
  
        svgNodes.attr('cx', d => d.x).attr('cy', d => d.y)
        svgNodesText.attr('x', d => d.x).attr('y', d => d.y)
  
        focusGroup
          .selectAll('circle')
          .attr('cx', d => d.x)
          .attr('cy', d => d.y)
  
        svgMenuText
          .select('menuCircle')
          .attr("transform", function (d) {
            var x1 = d.x
            var y1 = d.y
            return 'translate(' + x1 + ',' + y1 + ')';
          })
        
      },
  
  
      updateNodesWithText() {
        if (!this.flags.loaded) return
        const {
          $d3,
          config: {baseRadius, font, opacity},
          nodes,
          links,
          doubleClick,
          svgElements: {
            simulation,
            root,
            svgNodes,
            svgNodesText,
            boundDrag,
            scale
          },
          clickNode,
          focus,
          unfocus,
          updateAllOpacity,
          drawPattern,
          rightClickNode
        } = this
        // console.log('updateNodesWithText', nodes)
        updateAllOpacity()
  
        // console.log("节点更新测试222："+JSON.stringify(nodes))
  
        // update nodes
        //修改节点背景
        let _svgNodes = root
          .select('g.nodes')
          .selectAll('circle')
          .data(nodes)
          .join('circle')
          .attr('r', d => baseRadius + d.radius * 10)
          .attr('fill', d => (d.color ? d.color : scale(d.group)))
          // .attr('fill',"red")
          //.style('fill',function(d){ return "url(#" + "pattern" + d.id + ")"; })
          .attr('opacity', d => (d.opacity ))
          .attr('data-id', d => d.id)
          .attr('cx', d => d.x)
          .attr('cy', d => d.y)
          .call(boundDrag)
          .on('click', clickNode)
          .on('contextmenu', rightClickNode)
          .on('dblclick', doubleClick)
          .on('mouseover', focus)
          .on('mouseout', unfocus)
  
        _svgNodes.append('title').text(d => d.name)
        _svgNodes = _svgNodes.merge(svgNodes)
        this.svgElements.svgNodes = _svgNodes
  
        // update nodes text
        const _svgNodesText = root
          .select('g.nodes_text')
          .selectAll('text')
          .data(nodes)
          .join('text')
          .style('fill', d => (d.isDelete ? '#ffffff33' : '#ffffff'))
          .style('font', d => `${d.textSize}px ${font}`)
          .style('user-select', 'none')
          .attr('dominant-baseline', 'middle')
          .attr('text-anchor', 'middle')
          .attr('data-id', d => d.id)
          .text(d => d.name)
          .call(boundDrag)
          .on('click', clickNode)
          .on('contextmenu', rightClickNode)
          .on('dblclick', doubleClick)
          .on('mouseover', focus)
          .on('mouseout', unfocus)
          .merge(svgNodesText)
        this.svgElements.svgNodesText = _svgNodesText
  
        // console.log("652测试：" + JSON.stringify(_svgNodesText.selectAll('text')))
        // console.log("nodes处理结果qfvsd："+JSON.stringify(nodes))
  
        bus.$emit("updataSearchNodes", nodes)
        // console.log("搜索传输节点（发）："+JSON.stringify(nodes))
  
  
        simulation.nodes(nodes)
        simulation.force('link').links(links)
        simulation.alpha(1).restart()
      },
      updateLinksWithText() {
        if (!this.flags.loaded) return
        const {
          config: {fontSize, font, opacity},
          nodes,
          links,
          svgElements: {simulation, root, svgLinks, svgLinksText},
          setLinksPositionRaio,
          clickLink,
          drawPattern,
          updateAllOpacity
        } = this
        // console.log('updateLinksWithText', links)
        updateAllOpacity()
  
        // console.log("关系初始化25225："+JSON.stringify(links))
  
        // update links
        let _svgLinks = root
          .select('g.links')
          .selectAll('line')
          .data(links)
          .join('line')
          .attr('stroke-width', d => d.value)
          .attr('opacity', d => (d.opacity ))
          .attr('id', d => `link-${d.id}`)
          .attr('data-id', d => d.id)
          // .attr('marker-end', d => `url(#arrow-${d.id})`)
          .attr('marker-end', d => `url(#arrow)`)
          .on('click', clickLink)
        _svgLinks.append('title').text(d => d.name)
        _svgLinks = _svgLinks.merge(svgLinks)
        this.svgElements.svgLinks = _svgLinks
  
        // update links text
        const _svgLinksText = root
          .select('g.links_text')
          .selectAll('text')
          .data(links)
          .join('text')
          .style('fill', '#3aa6b5')
          .style('font', `${fontSize}px ${font}`)
          .attr('opacity', d => (d.opacity ))
          .style('user-select', 'none')
          .attr('dominant-baseline', 'middle')
          .attr('text-anchor', 'middle')
          .attr('data-id', d => d.id)
          // .text(d => d.name)
          .on('click', clickLink)
          .merge(svgLinksText)
        this.svgElements.svgLinksText = _svgLinksText
        simulation.nodes(nodes)
        simulation.force('link').links(links)
        simulation.alpha(1).restart()
        setLinksPositionRaio()
      },
      setLinksPositionRaio() {
        const {
          config: {baseRadius},
          links
        } = this
        links.forEach(link => {
          const {
            source: {radius: r1},
            target: {radius: r2}
          } = link
          link.positionRatio = (baseRadius + r1 * 20) / ((r1 + r2) * 30 + baseRadius)
        })
      },
      updateFocus() {
        const {
          config: {baseRadius},
          svgElements: {focusGroup: fg}
        } = this
        const originFocus = fg.selectAll('circle')
        fg.selectAll('circle')
          .data(this.nodes.filter(node => node.highLight || node.focus))
          .join('circle')
          .attr('r', d => baseRadius + d.radius * 10 + 5)
          .attr('fill', 'none')
          .attr('id', d => `focus-node-${d.id}`)
          .attr('cx', d => d.x)
          .attr('cy', d => d.y)
          .merge(originFocus)
      },
      /********** 外部节点操作 **********/
      createNode(node) {
        // console.log('[GraphBoard] createNode', node)
        const {
          nodes,
          updateNodesWithText,
          setNodeFocus,
          setFocus,
          layoutMode,
          restoreLayout
        } = this
        nodes.push(node)
        updateNodesWithText()
        setNodeFocus(node)
        setFocus(node)
        layoutMode === 'GRID' && restoreLayout(true)
      },
      createLink(link) {
  
        // console.log('[GraphBoard] createLink', link)
        const {links, updateLinksWithText} = this
        links.push(link)
        updateLinksWithText()
      },
      updateNode(node) {
        // console.log('[GraphBoard]', node)
        const {
          nodes,
          updateNodesWithText,
          setNodeFocus,
          updateFocus,
          layoutMode,
          restoreLayout
        } = this
        const _node = nodes.filter(_node => _node.id === node.id)[0]
        for (const prop in _node) {
          _node[prop] = node[prop]
        }
        updateNodesWithText()
        setNodeFocus(_node)
        updateFocus()
        layoutMode === 'GRID' && restoreLayout(true)
      },
      updateLink(link) {
        // console.log('[GraphBoard]', link)
        const _link = this.links.filter(_link => _link.id === link.id)[0]
        for (const prop in _link) {
          link[prop] = link[prop]
        }
        this.updateLinksWithText()
      },
      deleteNode(nodeId) {
        // console.log('[GraphBoard]', nodeId)
        const {
          nodes,
          links,
          updateNodesWithText,
          updateLinksWithText,
          clearFocus,
          layoutMode,
          restoreLayout
        } = this
        // delete node
        const node = nodes.filter(node => node.id === nodeId)[0]
        nodes.splice(nodes.indexOf(node), 1)
  
        // delenodes
        const _link = links.filter(_link => _link.to === node.id || _link.from === node.id)
        for (const prop in _link) {
          this.delelinks.push(_link[prop])
        }
        // delete links
        this.links = links.filter(
          ({from, to}) => from !== node.id && to !== node.id
        )
        updateNodesWithText()
        updateLinksWithText()
        clearFocus()
        layoutMode === 'GRID' && restoreLayout(true)
      },
      deleteLink(linkId) {
        // console.log('[GraphBoard]', linkId)
        const {links} = this
        const link = links.filter(link => link.id === linkId)[0]
        links.splice(links.indexOf(link), 1)
        this.updateLinksWithText()
      },
      updateData() {
        this.updateNodesWithText()
        this.updateLinksWithText()
      },
      /********** 外部图谱操作 **********/
      // 重置缩放
      resetZoom() {
        const {
          $d3,
          config,
          nodes,
          svgElements: {view, boundZoom}
        } = this
        const scale = calcScale(nodes, config)
        view
          .transition()
          .duration(750)
          .call(boundZoom.transform, $d3.zoomIdentity.scale(scale))
      },
      // 随机分布
      randomDisorder() {
        const {
          svgElements: {simulation}
        } = this
        if (this.layoutMode === 'FORCE') {
          this.nodes.forEach(node => {
            node.vx = node.vy = null
            node.x = node.y = null
            node.fx = node.fy = null
          })
          simulation.alphaTarget(0.3).restart()
        }
      },
      // 切换布局
      switchLayout(mode) {
        if (this.layoutMode !== mode) {
          this.layoutMode = mode
          this.restoreLayout()
        }
      },
      // 保存布局
      saveLayout() {
        const {layoutMode, nodes, layouts, updateLayout, projectInfo} = this
        const layoutNodes = nodes.map(({id, x, y}) => ({id, x, y}))
        // console.log(`saveLayout layout mode: ${layoutMode}`, layoutNodes)
        updateLayout({
          type: layoutMode,
          projectId: projectInfo.projectId,
          nodes: layoutNodes
        }).then(res => {
          if (res) {
            layouts[layoutMode].nodes = layoutNodes
          }
        })
      },
      // 恢复布局
      restoreLayout(bool) {
        const {
          config,
          layoutMode,
          layouts,
          nodes,
          pin,
          unPin,
          flags: {pinned},
          setLocked
        } = this
        
        if (
          bool ||
          (layoutMode === 'GRID' && layouts.GRID.nodes.length !== nodes.length)
        ) {
          layouts.GRID.nodes = getGridLayout(nodes, config)
          const layoutNodesMapper = {}
          console.log('layouts:', layouts)
          console.log('layoutMode:', layoutMode)
          layouts.GRID.nodes.forEach(({id, x, y}) => {
            layoutNodesMapper[id] = {x, y}
          })
          console.log(`restoreLayout ${layoutMode}:`, layoutNodesMapper)
    
          unPin()
          nodes.forEach(node => {
            if (Reflect.has(layoutNodesMapper, node.id)) {
              const {x, y} = layoutNodesMapper[node.id]
              node.x = x
              node.y = y
              if (pinned) {
                node.fx = x
                node.fy = y
              }
            }
          })
          setLocked(layoutMode === 'GRID')
          layoutMode === 'FORCE' ? unPin() : pin()
        }
        
      },
      // 选取类别
      selectGroups(groups) {
        // console.log('selectGroups', groups)
        this.setSelectedGroups(groups)
        this.updateNodesWithText()
        this.updateLinksWithText()
      },
      // 直接选中单个节点
      selectNode(nodeId) {
        // console.log('graph selectNode', nodeId)
        const node = this.getNodeById(nodeId)
        this.setFocus(node)
        this.setNodeFocus(node)
      },
      // 多个节点一次高亮
      highLightMultiple(nodeIds) {
        this.clearFocus()
        // console.log('highLightMultiple', nodeIds)
        const nodes = this.getNodesByIds(nodeIds)
        nodes.forEach(node => this.setNodeHighLight(node, true))
        this.updateHighLightNodes()
      },
      // 切换选中模式
      setSelectingMode(mode) {
        this.flags.selectingMode = mode
      },
      selectInSelectingMode() {
      },
      /********** 图谱操作 **********/
      // 设置高亮组
      setFocusNodes(nodeIds) {
        const {
          config: {baseRadius},
          focusNodes,
          svgElements: {focusGroup: fg},
          getNodesByIds,
          setNodeFocus
        } = this
        const targetNodes = getNodesByIds(nodeIds)
        targetNodes.forEach(node => setNodeFocus(node))
  
        fg.selectAll('circle')
          .data(focusNodes)
          .enter()
          .insert('circle')
          .attr('r', d => baseRadius + d.radius * 10 + 5)
          .attr('fill', 'none')
          .attr('id', d => `focus-node-${d.id}`)
          .attr('cx', d => d.x)
          .attr('cy', d => d.y)
      },
      // 清除高亮
      clearFocus() {
        this.svgElements.focusGroup.selectAll('circle').remove()
        this.svgElements.root.select('.menuCircle').remove()
        this.nodes.forEach(node => {
          this.setNodeHighLight(node, false)
          this.clearNodeFocus(node)
        })
      },
      // 固定节点
      pin() {
        const {
          nodes,
          flags: {pinned}
        } = this
        if (!pinned) {
          this.flags.pinned = true
          nodes.forEach(node => {
            node.fx = node.x
            node.fy = node.y
          })
        }
      },
      // 取消固定
      unPin() {
        const {
          svgElements: {simulation},
          nodes
        } = this
        this.flags.pinned = false
        nodes.forEach(node => {
          node.fx = null
          node.fy = null
        })
        simulation.alpha(1).restart()
      },
      dragMove(d) {
        d.dx += this.$d3.event.dx
        d.dy += this.$d3.event.dy
        this.$d3.select(this)
          .attr("transform", "translate(" + d.dx + "," + d.dy + ")")
      },
      addMyLinks(nodeOrId) {
        console.log('[GraphBoard]', nodeOrId)
        const {
          $d3,
          nodes,
          links,
          updateNodesWithText,
          updateLinksWithText,
          createNode,
          createLink,
          clearFocus,
          layoutMode,
          restoreLayout,
          getNodeById,
          isContains,
          projectInfo
        } = this
        const nodeId = nodeOrId.id
        const entityName = nodeOrId.name
        console.log("nodeOrID",nodeOrId)
        let isInclude = false
        // const projectId = this.projectId
        // Number(this.$route.params.projectId)
        const data = {
          "entityId": nodeId,
          "entityName": entityName,
          "fuzzyQuery": true,
          "projectId": nodeOrId.projectId
        }
        this.hasDoubleClicked.push(nodeId)
        this.entityQueryGlobal(data).then(res => {
          if (res) {
            res.nodes.forEach(node => {
              res.links.forEach(link => {
                // console.log("请求测试：向后端请求了9841："  + JSON.stringify(link))
                if (link.target === node.id) {
                  this.middleLinks.push(link)
                  console.log("链接测试9846415112link" + JSON.stringify(link))
                }
              })

              console.log("1271双击测试sadasd：" + node.id)

              if (getNodeById(node.id)) {
                getNodeById(node.id).isNew = false
                console.log("1427")
              } else {
                node.isNew = false
                node.isDelete = false
                node.opacity = false
                nodes.push(node)
                if (!this.selectedGroups.includes(node.group)) {
                  this.selectedGroups.push(node.group)
                }
                this.allNodes.push(node.id)
              }

            })

            bus.$emit("changeAllGroups", this.selectedGroups)

            // this.$emit('recieveGroup',this.selectedGroups)

            // this.links = this.middleLinks

            if (this.delelinks.length !== 0) {
              const _link = this.delelinks.filter(_link => _link.to === nodeId)

              if (_link.length !== 0) {
                for (const prop in _link) {
                  createLink(_link[prop])
                  this.delelinks.splice(this.delelinks.indexOf(_link[prop]), 1)
                }
              }
              this.addLoopNode(nodeId)
            }

            updateNodesWithText()
            updateLinksWithText()
            clearFocus()
            this.svgElements.root.select('.menuCircle').remove()
            layoutMode === 'GRID' && restoreLayout(true)
          }
        })
      // }
    },
      returnGroups() {
        return this.selectedGroups
      },
  
      addLoopNode(nodeId) {
        const {
          nodes,
          links,
          createNode,
          createLink,
        } = this
        const _linkto = this.delelinks.filter(_linkto => _linkto.from === nodeId)
  
  
        console.log("1230展开测试：" + JSON.stringify(_linkto))
  
        if (_linkto.length !== 0) {
          for (const prop in _linkto) {
            // delete nodes
            const nodeto = this.delenodes.filter(nodeto => nodeto.id === _linkto[prop].to)[0]
            if (typeof (nodeto) !== 'undefined') {
              // add nodes
              createNode(nodeto)
              this.delenodes.splice(this.delenodes.indexOf(nodeto), 1)
              // add links
              createLink(_linkto[prop])
              this.delelinks.splice(this.delelinks.indexOf(_linkto[prop]), 1)
              this.addLoopNode(_linkto[prop].to)
            }
          }
        }
      },
      //收起
      deleteMyLinks(nodeOrId) {
        // console.log('[delBoard]', nodeOrId)
        const {
          nodes,
          links,
          updateNodesWithText,
          updateLinksWithText,
          clearFocus,
          layoutMode,
          restoreLayout
        } = this
        // console.log('[delBoard]', nodes)
        const nodeId = nodeOrId.id
        const nodegroup = nodeOrId.group
        //收起的Links
        const node = nodes.filter(node => node.id === nodeId)[0]
        const _link = links.filter(_link => _link.from === nodeId)
  
        for (const prop in _link) {
          // delete links
          // links.splice(links.indexOf(_link[prop]), 1)
          this.delelinks.push(_link[prop])
        }
  
        this.deleLoopNode(nodeId)
        //console.log('[delelinks]',this.delelinks)
        updateNodesWithText()
        updateLinksWithText()
        clearFocus()
        this.svgElements.root.select('.menuCircle').remove()
        layoutMode === 'GRID' && restoreLayout(true)
      },
      deleLoopNode(nodeId) {
        const {
          nodes,
          links,
        } = this
  
        console.log("收起测试" + JSON.stringify(this.delelinks))
  
        const _linkto = links.filter(_linkto => _linkto.from === nodeId)
  
        if (_linkto.length !== 0) {
          for (const prop in _linkto) {
            // delete links
            links.splice(links.indexOf(_linkto[prop]), 1)
            this.delelinks.push(_linkto[prop])
            // delete nodes
            const nodeto = nodes.filter(nodeto => nodeto.id === _linkto[prop].to)[0]
  
            if (typeof (nodeto) !== 'undefined') {
              nodes.splice(nodes.indexOf(nodeto), 1)
              this.delenodes.push(nodeto)
              this.deleLoopNode(_linkto[prop].to)
            }
          }
        }
      },
      // 点击节点
      clickNode(e) {
        console.log("输出鼠标按键编号:" + e.which);
  
        // console.log("1298点击测试" + JSON.stringify(e))
        const {
          config: {baseRadius},
          nodes,
          flags: {enableFocus},
          svgElements: {root, focusGroup: fg},
          getNodeById,
          getProjectInfoAct
        } = this
        this.clearFocus()
        const id = Number(e.target.attributes['data-id'].value)
  
        const node = getNodeById(id)
        
        this.projectId = node.projectId
  
        console.log("点击了节点561651：" + JSON.stringify(node))
        this.selectedItem.type == 'node'
        this.selectedItem.item == node
        console.log("获取项目信息：" + this.projectId)
        node.textSize = parseInt(node.textSize)
        // console.log("点击测试85556:"+JSON.stringify(node))
        // node.group = JSON.parse(node.group)
        // console.log("点击测试85557："+JSON.stringify(node))
        if (node.isDelete)
          return
  
        //const originFocus = fg.selectAll('circle')
        //console.log(`click node: id=${id}, `, root)
  
  
        if (this.flags.selectingMode) {
          this.flags.selectingMode = false
          console.log("数据测试111：执行了节点点击方法")
          this.$emit('editor-action', 'catchNode', node,)
        } else {
          this.setNodeFocus(node)
  
          // //wx
          // this.toggleCircle_wx(this.svgElements.svg, e)
          this.$emit('editor-action', 'selectNode', {...node})
          this.$emit('msg-action', 'setSituationid', {...node})
          // console.log(`click node t2: id=${node.id}, `, node)
          //调用收起、展开功能
          // this.toggleCircle(root, node)
  
          //圆环菜单构建方法
          // this.toggleCircle_wx(root, node)
  
        }
      },
      // 点击关系
      clickLink(e) {
        if (this.flags.selectingMode) return
        const id = Number(e.target.attributes['data-id'].value)
        const link = this.getLinkById(id)
        // console.log(`click link: id=${id}, `, link)
  
        this.clearFocus()
        this.$emit('editor-action', 'selectLink', {...link})
      },
      clickView() {
        if (this.flags.selectingMode) return
        this.clearFocus()
        this.$emit('editor-action', 'selectNone')
      },
  
      // 聚焦(高亮显示)
      focus(e) {
        const {
          getNodeById,
          setFocus,
          flags: {enableFocus},
          root,
        } = this
        if (!enableFocus) return
        const id = Number(e.target.attributes['data-id'].value)
        const targetNode = getNodeById(id)
  
        if (targetNode.haveCircle)
          this.toggleCircle_wx(root, targetNode)
  
        //console.log(`focus: id=${id}, `, targetNode)
        if (!targetNode || targetNode.isDelete) return
  
        setFocus(targetNode)
      },
      // 取消聚焦
      unfocus(e) {
  
        // console.log("焦点移除" + JSON.stringify(e))
        const {
          flags: {enableFocus},
          _unfocus
        } = this
        if (!enableFocus) return
  
        const id = Number(e.target.attributes['data-id'].value)
        _unfocus(id)
      },
      _unfocus(id) {
        const {
          svgElements: {focusGroup: fg},
          getNodeById,
          setNodeHighLight
        } = this
        const targetNode = getNodeById(id)
        setNodeHighLight(targetNode, false)
        const focusNode = fg.select(`#focus-node-${id}`)
        if (!focusNode.empty()) {
          if (targetNode && targetNode.focus) return
  
          focusNode.remove()
  
        }
      },
      /********** 力导图绑定事件 **********/
      // 拖曳设置
      setDrag(simulation) {
  
        const {setEnableFocus, flags, nodes} = this
  
        const start = (e, d) => {
          if (!e.active) simulation.alphaTarget(0.3).restart()
          setEnableFocus(false)
          if (flags.locked) {
            const group = d.group
            nodes
              .filter(node => node.group === group)
              .forEach(node => {
                node.fx = d.x
              })
  
          } else {
            d.fx = d.x
            d.fy = d.y
          }
        }
  
        const drag = (e, d) => {
          if (flags.locked) {
            const group = d.group
            nodes
              .filter(node => node.group === group)
              .forEach(node => {
                node.fx = e.x
              })
          } else {
            d.fx = e.x
            d.fy = e.y
          }
        }
  
        const end = (e, d) => {
          setEnableFocus(true)
          if (flags.locked) return
          if (!e.active) simulation.alphaTarget(0)
          d.x = d.fx
          d.y = d.fy
          if (!flags.pinned) {
            d.fx = null
            d.fy = null
          }
        }
  
        const boundDrag = this.$d3
          .drag()
          .on('start', start)
          .on('drag', drag)
          .on('end', end)
        this.svgElements.boundDrag = boundDrag
  
        return boundDrag
      },
      // 缩放&平移设置
      setZoom(root) {
        const boundZoom = this.$d3.zoom().on('zoom', e => {
          root.attr('transform', e.transform)
        })
        this.svgElements.boundZoom = boundZoom
        return boundZoom
      },
      /********** 数据属性操作 **********/
      // 根据 id 查找节点
      getNodeById(id) {
        return this.nodes.filter(node => node.id === id)[0]
      },
      getNode(nodeOrId) {
        return typeof nodeOrId === 'object'
          ? nodeOrId
          : this.getNodeById(nodeOrId)
      },
      // 根据 id 查找关系
      getLinkById(id) {
        return this.links.filter(link => link.id === id)[0]
      },
      // 设置选中组别
      setSelectedGroups(groups) {
        // console.log("组别测试sadas" + JSON.stringify(this.selectedGroups))
        this.selectedGroups = groups
        // console.log("组别测试sadas" + JSON.stringify(this.selectedGroups))
        this.updateAllOpacity()
      },
      // updateAllOpacity() {
      //   const {selectedGroups: groups, nodes, links} = this
      //   nodes.forEach(node => {
      //     // node.color = node.color ? "#" + node.color.slice(1, node.color.length) + "33" : this.svgElements.scale(node.group)+"33"
      //   })
      //   links.forEach(link => {
      //     link.opacity = link.source.opacity || link.target.opacity
      //   })
      // },
  
      updateAllOpacity() {
        const {selectedGroups: groups, nodes, links} = this
        if (nodes) {
          nodes.forEach(node => {
            node.opacity = !groups.includes(node.group)
          })
          links.forEach(link => {
            link.opacity = link.source.opacity || link.target.opacity
          })
        }
      },
  
      // 设置节点高亮标志
      setNodeHighLight(nodeOrId, bool = false) {
        const node = this.getNode(nodeOrId)
        node.highLight = bool
      },
      setNodeFocus(nodeOrId) {
        const {
          nodes,
          flags: {singleFocus},
          _unfocus,
          getNode
        } = this
        const node = getNode(nodeOrId)
  
        if (singleFocus) {
          nodes
            .filter(n => n !== node)
            .forEach(node => {
              if (node.focus) {
                node.focus = false
                _unfocus(node.id)
              }
            })
        }
        node.focus = true
      },
      clearNodeFocus(nodeOrId) {
        const node = this.getNode(nodeOrId)
        node.focus = false
      },
      getNodesByIds(ids) {
        return this.nodes.filter(node => ids.includes(node.id))
      },
      /********** 图谱标志 **********/
      setEnableFocus(bool = true) {
        this.flags.enableFocus = bool
      },
      setLocked(bool = false) {
        this.flags.locked = bool
      },
      /********** 图谱标志 **********/
      exportPng() {
        const projectName = this.projectInfo.name
        const svg = this.svgElements.svg
        this.saveAsPng({projectName, svg})
  
      },
      exportXml() {
        const {projectId, name} = this.projectInfo
        console.log("信息获取789432：" + projectId + "  " + name)
        this.saveAsXml({projectId, name})
      },
     
      doubleClick(e) {
        const {
          getNodeById,
          addMyLinks
        } = this
  
        this.clearFocus()
        const id = Number(e.target.attributes['data-id'].value)
        const node = getNodeById(id)
        
        if (!node.isDelete) {
          addMyLinks(node)
        }
        console.log("1843执行了双击" + node.id)
      },
  
      //wx2.13
      setCirileMenu(root) {
        const menuGroup = root
          .append('g')
          .attr('class', 'circle-menu')
          .attr("")
        this.svgElements.menuGroup = menuGroup
      },
  
      // searchAddOne(data) {
        
      //   this.entityQueryGlobal(data).then((res) => {
      //       console.log("searchAddOne搜索结果"+JSON.stringify(res))
      //       this.graphData = res
      //       const nodeSearch = this.$refs.searchBoard
      //       nodeSearch.mountGraphData(this.graphData,this.projectInfo) 
      //     })
        
      // },
  
      isContains(returnNode) {
        if (this.nodes.contain(returnNode))
          return true;
        else
          return false;
      },
  
  
    },
    async mounted() {
    
    // const projectId = 0
    // const projectId = Number(this.$route.params.projectId)
    // const projectInfo = await this.getProjectInfoAct(projectId)
    // if (!projectInfo) {
    //   // warning
    //   return
    // }
    // this.projectInfo = projectInfo

    //初始化图谱数据
    const graphData = await this.getProjectGraphDataAct(projectId)

    // const graphData = deepCopy(_graphData)
    if (!graphData) {
      // warning
      return
    }
    this.graphData = graphData
    console.log("初始化图谱数据结果09123012："+JSON.stringify(this.graphData))

    const nodeSearch = this.$refs.searchBoard
    nodeSearch.mountGraphData(this.graphData, this.projectInfo)

    console.log('projectId:', projectId)
    console.log('projectInfo:', projectInfo)
    console.log('graphData:', graphData)
  }
  }
  </script>
  
  <style scoped>
  #graph {
    width: 100%;
    height: 100%;
  }
  </style>
  